﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Management;
using System.Text;
using System.Windows.Forms;
using Microsoft.SharePoint.Administration;
using Smart.Common;
using Smart.SQL;
using Smart.Util.UI.Properties;

namespace Smart.Util.UI
{
	public partial class MainForm : Form
	{
		public MainForm()
		{
			InitializeComponent();
		}

		private void MainForm_Load(object sender, EventArgs e)
		{
			tbSQLServer.Text = Environment.MachineName;
			tbDatabase.Text = "WSS_Content";
			tbMasterKey.Text = "P@$$w0rd";
		}

		private bool _generateProviderName = true;
		private bool _ignoreProviderNameChange;

		private void tbDatabase_TextChanged(object sender, EventArgs e)
		{
			if (!_generateProviderName) return;

			_ignoreProviderNameChange = true;
			tbProvider.Text = String.Concat("FilestreamProvider", tbDatabase.Text);
			_ignoreProviderNameChange = false;
		}

		private void tbProvider_TextChanged(object sender, EventArgs e)
		{
			if (_ignoreProviderNameChange)
				return;

			_generateProviderName = false;
		}

		private bool _isValid;

		private bool CheckIsValid()
		{
			_isValid = true;

			return
				ValidateChildren() && _isValid;
		}

		private void TextBoxValidating(object sender, CancelEventArgs e)
		{
			var tb = (TextBox)sender;

			_isValid = _isValid && !tb.Text.IsNullOrEmpty();

			ep.SetError(tb, tb.Text.IsNullOrEmpty() ? Resources.ErrorProvider_FieldIsEmpty : null);
		}

		private void TextBoxValidatingBlob(object sender, CancelEventArgs e)
		{
			var tb = (TextBox)sender;

			var empty = tb.Text.IsNullOrEmpty();

			_isValid = _isValid && !empty;

			ep.SetError(
				tb,
				empty
					? Resources.ErrorProvider_FieldIsEmpty
					: System.IO.Directory.Exists(tb.Text)
						  ? Resources.ErrorProvider_FolderShouldNotExists
						  : null);
		}

		private SqlSettings GetSettings()
		{
			return new SqlSettings { SqlServerMain = tbSQLServer.Text, DatabaseName = tbDatabase.Text };
		}

		private void GetSqlServerInstance(out string serverName, out string instanceName)
		{
			SqlExts.SplitServerName(tbSQLServer.Text, out serverName, out instanceName);

			instanceName = SqlExts.GetInstanceName(instanceName);
		}

		private bool EnableFileStreamViaWMI(string instanceName)
		{
			ManagementObject fsInstance = null;
			PropertyDataCollection properties = null;

			try
			{
				var mpath10 = String.Concat(@"\\", Environment.MachineName, @"\root\Microsoft\SqlServer\ComputerManagement10:FilestreamSettings='", instanceName, "'");

				fsInstance = new ManagementObject(mpath10);

				properties = fsInstance.SystemProperties;
			}
			catch
			{
				if (fsInstance != null)
				{
					fsInstance.Dispose();
					fsInstance = null;
				}
			}

			if (fsInstance == null)
			{
				try
				{
					var mpath11 = String.Concat(@"\\", Environment.MachineName, @"\root\Microsoft\SqlServer\ComputerManagement11:FilestreamSettings='", instanceName, "'");

					fsInstance = new ManagementObject(mpath11);

					properties = fsInstance.SystemProperties;
				}
				catch
				{
					if (fsInstance != null)
					{
						fsInstance.Dispose();
						fsInstance = null;
					}
				}
			}

			if (fsInstance == null)
			{
				return false;
			}

			try
			{
				var inParam = fsInstance.GetMethodParameters("EnableFilestream");

				inParam["AccessLevel"] = 3;
				inParam["ShareName"] = String.Empty;

				var outParam = fsInstance.InvokeMethod("EnableFilestream", inParam, null);

				var returnValue = outParam != null ? outParam["returnValue"] : -1;

				return
					!object.Equals(returnValue, 0);
			}
			catch
			{
				return false;
			}
			finally
			{
				if (fsInstance != null)
				{
					fsInstance.Dispose();
					fsInstance = null;
				}
			}
		}

		private void EnableFileStream_Click(object sender, EventArgs e)
		{
			if (!CheckIsValid())
				return;

			string serverName;
			string instanceName;

			GetSqlServerInstance(out serverName, out instanceName);

			var succeeded = true;

			//if (!EH.ExecCmd(@"cscript", String.Format(@"filestream_enable.vbs /Machine:{0} /Instance:{1} /Level:3 /Version:{2}", serverName, instanceName, rbSQL11.Checked ? "11" : "10")))
			if (!EnableFileStreamViaWMI(instanceName))
			{
				succeeded = false;
				MessageBox.Show(this, Resources.EnableFileStream_WMIErrorMessage, Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}

			using (var sql = new SqlSession(GetSettings()))
			{
				if (!sql.ExecuteDDL("EXEC sp_configure filestream_access_level, 2\nRECONFIGURE"))
				{
					succeeded = false;
					MessageBox.Show(this, Resources.EnableFileStream_SqlErrorMessage, Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
			}

			if (succeeded)
				MessageBox.Show(this, Resources.EnableFileStream_SucceededMessage, Resources.SucceededCaption, MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
		}

		const string SetMasterKeyCmd = @"if not exists (select * from sys.symmetric_keys where name = N'##MS_DatabaseMasterKey##') create master key encryption by password = N{0}";
		const string AddFileGroupCmd = @"if not exists (select groupname from sysfilegroups where groupname=N'RBSFilestreamProvider') alter database [{0}] add filegroup RBSFilestreamProvider contains filestream";
		const string AddFileToGroupCmd = @"alter database [{0}] add file (name = RBSFilestreamFile, filename = {1}) to filegroup RBSFilestreamProvider";

		const string DisableShortFileNamesArgs1 = @"8dot3name set 2";
		const string DisableShortFileNamesArgs2 = @"8dot3name set {0}: 1";

		const string DisableLastAccessArgs = @"behavior set DisableLastAccess 1";

		private void ProvisionRBS_Click(object sender, EventArgs e)
		{
			if (!CheckIsValid())
				return;

			var masterKey = tbMasterKey.Text;
			var database = tbDatabase.Text;
			var path = tbBlobPath.Text;

			var succeeded = true;

			if (cbDSFNC.Checked && path.Contains(":"))
			{
				var volume = path.SubstringTo(":");

				if (!EH.ExecCmd(@"fsutil", DisableShortFileNamesArgs1))
				{
					succeeded = false;
					MessageBox.Show(this, Resources.ProvisionRBS_DisableShortFileNames1ErrorMessage, Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}

				if (!EH.ExecCmd(@"fsutil", String.Format(DisableShortFileNamesArgs2, volume)))
				{
					succeeded = false;
					MessageBox.Show(String.Format(Resources.ProvisionRBS_DisableShortFileNames2ErrorMessage, volume), Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
			}

			if (cbDLA.Checked && path.Contains(":"))
			{
				if (!EH.ExecCmd(@"fsutil", DisableLastAccessArgs))
				{
					succeeded = false;
					MessageBox.Show(this, Resources.ProvisionRBS_DisableLastAccessErrorMessage, Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
			}

			using (var sql = new SqlSession(GetSettings()))
			{
				if (!sql.ExecuteDDL(String.Format(SetMasterKeyCmd, masterKey.IncapsulateSql())))
				{
					succeeded = false;
					MessageBox.Show(this, Resources.ProvisionRBS_MasterKeyErrorMessage, Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}

				if (!sql.ExecuteDDL(String.Format(AddFileGroupCmd, database)))
				{
					succeeded = false;
					MessageBox.Show(this, Resources.ProvisionRBS_FileGroupErrorMessage, Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}

				if (!sql.ExecuteDDL(String.Format(AddFileToGroupCmd, database, path.IncapsulateSql())))
				{
					succeeded = false;
					MessageBox.Show(this, Resources.ProvisionRBS_FileErrorMessage, Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
				}
			}

			if (succeeded)
				MessageBox.Show(this, Resources.ProvisionRBS_SucceededMessage, Resources.SucceededCaption, MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
		}

		private string GetPackage()
		{
			var package = "RBS_1050SP1_X64.msi";

			if (rbSQL1050.Checked)
			{
				package = "RBS_1050_X64.msi";
			}
			else if (rbSQL1050SP1.Checked)
			{
				package = "RBS_1050SP1_X64.msi";
			}
			else if (rbSQL11.Checked)
			{
				package = "RBS_11_X64.msi";
			}

			return package;
		}

		const string InstallFirstArgs = @"/qn /lvx* rbs_install_log.txt /i ""{0}"" TRUSTSERVERCERTIFICATE=true FILEGROUP=PRIMARY DBNAME=""{1}"" DBINSTANCE=""{2}"" FILESTREAMFILEGROUP=RBSFilestreamProvider FILESTREAMSTORENAME={3}";

		private void InstallFirst_Click(object sender, EventArgs e)
		{
			if (!CheckIsValid())
				return;

			var package = GetPackage();
			var database = tbDatabase.Text;
			var server = tbSQLServer.Text;
			var provider = tbProvider.Text;

			var succeeded = true;

			if (!EH.ExecCmd(@"msiexec", String.Format(InstallFirstArgs, package, database, server, provider)))
			{
				succeeded = false;
				MessageBox.Show(String.Format(Resources.InstallFirst_MsiErrorMessage, package), Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}

			if (succeeded)
				MessageBox.Show(this, Resources.InstallFirst_SucceededMessage, Resources.SucceededCaption, MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
		}

		const string InstallOtherArgs = @"/qn /lvx* rbs_install_log.txt /i ""{0}"" DBNAME=""{1}"" DBINSTANCE=""{2}"" ADDLOCAL=Client,Docs,Maintainer,ServerScript,FilestreamClient,FilestreamServer";

		private void InstallOther_Click(object sender, EventArgs e)
		{
			if (!CheckIsValid())
				return;

			var package = GetPackage();
			var database = tbDatabase.Text;
			var server = tbSQLServer.Text;
			var provider = tbProvider.Text;

			var succeeded = true;

			if (!EH.ExecCmd(@"msiexec", String.Format(InstallOtherArgs, package, database, server, provider)))
			{
				succeeded = false;
				MessageBox.Show(String.Format(Resources.InstallOther_MsiErrorMessage, package), Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
			}

			if (succeeded)
				MessageBox.Show(this, Resources.InstallOther_SucceededMessage, Resources.SucceededCaption, MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
		}

		private void EnableRBS_Click(object sender, EventArgs e)
		{
			if (!CheckIsValid())
				return;

			if (!SPFarm.Joined)
			{
				MessageBox.Show(this, Resources.EnableRBS_NoFarmErrorMessage, Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}

			var farm = SPFarm.Local;

			var service = farm.Services.GetValue<SPWebService>();

			if (service == null)
			{
				MessageBox.Show(this, Resources.EnableRBS_NoWebServiceErrorMessage, Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}

			var applications = service.WebApplications;

			if (applications == null)
			{
				MessageBox.Show(this, Resources.EnableRBS_NoWebApplicationsErrorMessage, Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}

			var databaseName = tbDatabase.Text;
			var provider = tbProvider.Text;

			var found = false;

			foreach (var application in applications)
			{
				foreach (var database in application.ContentDatabases)
				{
					if (string.Equals(databaseName, database.Name, StringComparison.OrdinalIgnoreCase))
					{
						found = true;

						try
						{
							var rbss = database.RemoteBlobStorageSettings;

							rbss.Installed();
							rbss.Enable();
							rbss.SetActiveProviderName(provider);
						}
						catch
						{
							MessageBox.Show(this, Resources.EnableRBS_ConfigureErrorMessage, Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
							return;
						}
					}
				}
			}

			if (!found)
			{
				MessageBox.Show(this, Resources.EnableRBS_DatabaseNotFoundErrorMessage, Resources.ErrorCaption, MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}

			MessageBox.Show(this, Resources.EnableRBS_SucceededMessage, Resources.SucceededCaption, MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
		}
	}
}
